package com.opennews.openplatform.myspringbootcore.util;

import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;

public class DateHelper {
    public final static String LONG_DATE = "yyyy-MM-dd";
    public final static String LONG_DATE_TIME = "yyyy-MM-dd HH:mm:ss";
    public final static String LONG_TIMESTAMP = "yyyyMMddHHmmSSS";
    public final static String UTC_DATE_TIME = "yyyy-MM-dd'T'HH:mm";
    public final static String UTC_LONG_DATE_TIME = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    // yyyy-MM-dd'T'HH:mm:ss'Z'
    private static final DateTimeFormatter UTC_FORMATTER = DateTimeFormatter.ofPattern(UTC_LONG_DATE_TIME);

    /**
     * Gets current system date.
     *
     * @return The Date instance of current date.
     */
    public static LocalDateTime getCurrentLocalDateTime() {
        return LocalDateTime.now();
    }

    public static String convertLocalDateTimeToUTCString(LocalDateTime dateTime) {
        // Gets the default time zone.
        TimeZone defaultTimeZone = TimeZone.getDefault();

        // Converts TimeZone to ZoneId.
        ZoneId zoneId = defaultTimeZone.toZoneId();

        // Converts LocalDateTime in the default time zone ZonedDateTime
        ZonedDateTime localDateTimeInDefaultZone = dateTime.atZone(zoneId);

        // Converts the ZonedDateTime to UTC.
        ZonedDateTime utcDateTime = localDateTimeInDefaultZone.withZoneSameInstant(ZoneId.of("UTC"));

        // Formats the UTC ZonedDateTime to the desired UTC format.
        return utcDateTime.format(UTC_FORMATTER);
    }

    public static LocalDateTime convertUTCStringToLocalDateTime(String dateStr) {
        // Parses the UTC string into a ZonedDateTime object (assuming UTC)
        ZonedDateTime utcDateTime = ZonedDateTime.parse(dateStr.trim());

        // Converts UTC ZonedDateTime to the system's default time zone.
        ZoneId defaultZoneId = ZoneId.systemDefault();
        ZonedDateTime localDateTimeInDefaultZone = utcDateTime.withZoneSameInstant(defaultZoneId);

        // Convert ZonedDateTime (in default zone) to LocalDateTime (without time zone).
        return localDateTimeInDefaultZone.toLocalDateTime();
    }

    public static String format(LocalDateTime dateTime, String format) {
        return dateTime.format(DateTimeFormatter.ofPattern(format));
    }

    /**
     * Parses the input string to LocalDateTime with a specific format.
     *
     * @param dateStr    The string containing the date value.
     * @param dateFormat The date format.
     * @return The LocalDateTime instance.
     */
    public static LocalDateTime parseToLocalDateTime(String dateStr, String dateFormat) {
        return SharedUtil.isNullOrEmpty(dateStr) || SharedUtil.isNullOrEmpty(dateFormat) ? null : LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern(dateFormat));
    }

    /**
     * Parses the input string to LocalDateTime with specific format.
     *
     * @param dateStr The string containing the date value.
     * @return The LocalDateTime instance.
     */
    public static LocalDateTime parseFromLongDateTime(String dateStr) {
        if (dateStr.contains("T") || dateStr.endsWith("Z")) {
            return convertUTCStringToLocalDateTime(dateStr);
        }

        return parseToLocalDateTime(dateStr, LONG_DATE_TIME);
    }

    /**
     * Gets the first day of the month based on the provided date.
     * Example: If date is 2019-12-13, the return value is 2019-12-01.
     *
     * @param date Any day in a month.
     * @return The first day of the month.
     */
    public static LocalDateTime getFirstDayOfMonth(LocalDateTime date) {
        return date.withDayOfMonth(1);
    }

    /**
     * Gets the first day of the month based on the provided year and month.
     *
     * @param year  The year.
     * @param month The month.
     * @return The first day of the month in the year.
     */
    public static LocalDateTime getFirstDayOfMonth(int year, int month) {
        return LocalDateTime.of(year, month, 1, 0, 0);
    }

    /**
     * Gets the last day of the month based on the provided year and month.
     *
     * @param year  The year.
     * @param month The month.
     * @return The last day of the month in the year.
     */
    public static LocalDateTime getLastDayOfMonth(int year, int month) {
        return getFirstDayOfMonth(year, month).withDayOfMonth(YearMonth.of(year, month).lengthOfMonth());
    }

    /**
     * Gets the first day of the next month based on the provided year and month.
     *
     * @param year  The year.
     * @param month The month.
     * @return The first day of the next month in the year.
     */
    public static LocalDateTime getFirstDayOfNextMonth(int year, int month) {
        return getFirstDayOfMonth(year, month).plusMonths(1).withDayOfMonth(1);
    }
}
